package service

import (
	"sync"

	"gitee.com/crack007/goose/gscore/server"
	"gitee.com/crack007/goose/gslog"
	"gitee.com/crack007/proxy/src/proxy/protocol"
)

type sessionState int

const (
	SessionOpen sessionState = iota
	SessionClosed
)

type ProxySession struct {
	clientSession    *server.Session // 和客户端构建的会话
	iProtocol        protocol.IProtocol
	clientMsgChan    chan []byte // 客户端发送消息队列
	proxyConnectChan chan struct{}
	serverSession    *server.Client // 和目标服务器构建的会话
	serverHost       string
	serverPort       uint16
	once             sync.Once
	lock             sync.RWMutex
	sessionState     sessionState
}

func (s *ProxySession) SessionOpen(iProtocol protocol.IProtocol, host string, port uint16) {
	s.iProtocol = iProtocol
	s.serverPort = port
	s.serverHost = host
	s.iProtocol.Init(s.clientSession)
	s.sessionState = SessionOpen
	gslog.Debug("[%s] Session Opened.", s.iProtocol.GetSessionId())

	go s.connectServer()
	go s.processClient()
}

// 处理来自客户端的消息，转发给目标服务器
func (s *ProxySession) processClient() {
	defer func() {
		i := recover()
		if _, ok := i.(error); ok {
			gslog.Error(i)
		}

		s.Close()
	}()

	<-s.proxyConnectChan
	for data := range s.clientMsgChan {
		s.serverSession.Send(data)
		s.iProtocol.ProcessClient(data)
	}
}

func (s *ProxySession) SendMsg(data []byte) {
	s.lock.Lock()
	defer s.lock.Unlock()
	if s.sessionState == SessionClosed {
		return
	}

	s.clientMsgChan <- data
}

// 连接目标服务器，需要将接收到的数据转发给客户端
func (s *ProxySession) connectServer() {
	clientBuilder := server.NewClientBuilder(s.serverHost, s.serverPort)
	clientBuilder.OnMessage(func(data []byte) {
		s.clientSession.AsyncSend(data)
		s.iProtocol.ProcessServer(s.serverSession, data)
	})

	clientBuilder.SetReconnectInterval(3)
	clientBuilder.OnConnect(func(client *server.Client) {
		s.proxyConnectChan <- struct{}{}
	})

	clientBuilder.OnError(func(client *server.Client, err error) {
		s.clientSession.Close()
		s.Close()
	})

	s.serverSession = clientBuilder.Build()
	s.serverSession.Start()
}

func (s *ProxySession) Close() {
	s.once.Do(func() {
		s.lock.Lock()
		defer s.lock.Unlock()

		gslog.Debug("[%s] Session Closed.", s.iProtocol.GetSessionId())
		s.sessionState = SessionClosed
		close(s.clientMsgChan)
		s.serverSession.Close()
	})
}
